// Package controller ORM的数据库连接函数
// xorm.go 使用github.com/xormplus/xorm第三方库连接数据库
// 注意：暂时不支持oracle 虽然这边把oracle的连接写进来了
package controller

import (
	"fmt"
	"time"

	"ginbase/zConfig"

	"github.com/xormplus/xorm"

	_ "github.com/denisenkom/go-mssqldb" // sql server驱动
	_ "github.com/go-sql-driver/mysql"   // mysql驱动
	_ "github.com/lib/pq"                // postgresql驱动
	_ "github.com/mattn/go-sqlite3"      // sqlite3驱动
)

// ConnectEngine 连接数据库
func ConnectEngine(dbconfigs []*zConfig.ServiceDB, key string) *xorm.Engine {
    for _, c := range dbconfigs {
        // 跳过不是需要连接的数据库
        if c.Key != key {
            continue
        }
        // 连接参数
        var dataSourceName string
        // 根据不同的数据库
        switch c.Type {
        case "sqlite3":
            dataSourceName = fmt.Sprintf(sqlite3SourcePattern, c.Host)
        case "mysql":
            dataSourceName = fmt.Sprintf(mysqlSourcePattern, c.User, c.Pwd, c.Host, c.Instance)
        case "oracle":
            dataSourceName = fmt.Sprintf(oracleSourcePattern, c.User, c.Pwd, c.Host, c.Instance)
        case "postgresql", "postgres":
            dataSourceName = fmt.Sprintf(postgresqlSourcePattern, c.Host, c.Port, c.User, c.Pwd, c.Instance)
        case "sqlserver":
            dataSourceName = fmt.Sprintf(sqlserverSourcePattern, c.Host, c.Port, c.Instance, c.User, c.Pwd)
        default:
            dataSourceName = ""
        }
        // 连接数据库
        db, err := xorm.NewEngine(c.Driver, dataSourceName)
        dealError(err, "连接数据库失败")
        // sqlite3读写分离
        if "sqlite3" == c.Type {
            if "wal" == c.Mode {
                db.Exec("pragma journal_mode=wal")
            }
            db.SetMaxOpenConns(1)
        }
        // 初始化SqlMap配置文件
        err = db.RegisterSqlTemplate(xorm.Pongo2("./config/sql/", ".stpl"))
        dealError(err, "初始化SqlMap配置文件失败")
        // 初始化SqlTemplate模板
        err = db.RegisterSqlMap(xorm.Xml("./config/sql/", ".xml"))
        dealError(err, "初始化SqlTemplate模板失败")
        // 设置时区
        setTimezoneToShanghai(db)
        // 返回数据库连接
        return db
    }
    // 全都没有匹配到 返回空
    return nil
}

// ConnectDBs 连接数据库
func ConnectEngines(dbconfigs []*zConfig.ServiceDB) []*xorm.Engine {
    var dbs []*xorm.Engine

    for _, c := range dbconfigs {
        // 连接参数
        var dataSourceName string
        // 根据不同的数据库
        switch c.Type {
        case "sqlite3":
            dataSourceName = fmt.Sprintf(sqlite3SourcePattern, c.Host)
        case "mysql":
            dataSourceName = fmt.Sprintf(mysqlSourcePattern, c.User, c.Pwd, c.Host, c.Instance)
        case "oracle":
            dataSourceName = fmt.Sprintf(oracleSourcePattern, c.User, c.Pwd, c.Host, c.Instance)
        case "postgresql", "postgres":
            dataSourceName = fmt.Sprintf(postgresqlSourcePattern, c.Host, c.Port, c.User, c.Pwd, c.Instance)
        case "sqlserver":
            dataSourceName = fmt.Sprintf(sqlserverSourcePattern, c.Host, c.Port, c.Instance, c.User, c.Pwd)
        default:
            dataSourceName = ""
        }
        // 连接数据库
        db, err := xorm.NewEngine(c.Driver, dataSourceName)
        dealError(err, "连接数据库失败")
        // sqlite3读写分离
        if "sqlite3" == c.Type {
            if "wal" == c.Mode {
                db.Exec("pragma journal_mode=wal")
            }
            db.SetMaxOpenConns(1)
        }
        // 初始化SqlMap配置文件
        err = db.RegisterSqlTemplate(xorm.Pongo2("./config/sql/", ".stpl"))
        dealError(err, "初始化SqlMap配置文件失败")
        // 初始化SqlTemplate模板
        err = db.RegisterSqlMap(xorm.Xml("./config/sql/", ".xml"))
        dealError(err, "初始化SqlTemplate模板失败")
        dbs = append(dbs, db)
    }
    // 设置时区
    setTimezoneToShanghai(dbs...)
    return dbs
}

// CloseEngine 关闭数据库连接
func CloseEngine(dbs ...*xorm.Engine) {
    for _, db := range dbs {
        db.Close()
    }
}

// 设置数据库时区到上海
func setTimezoneToShanghai(dbs ...*xorm.Engine) {
    loc, _ := time.LoadLocation("Asia/Shanghai")
    for _, db := range dbs {
        db.SetTZDatabase(loc)
        db.SetTZLocation(loc)
    }
}